#include <boost/date_time/posix_time/posix_time.hpp>
#include "kernel.h"
#include "wtflogic.h"
#include "input.h"

using namespace WtfEngine;
using namespace Wtf;
using namespace boost::posix_time;

//#define TIME_HACK

void Kernel::RunGameLoop() {
	std::set<TaskCore*>::iterator	task;
	std::set<TaskCore*>				tasks;
	TaskCore*						pTask;
	Event::tRef rFrameStart, rFrameEnd;

	this->mLocalStartTime = ((tGameTime)microsec_clock::local_time().time_of_day().total_microseconds())
		/ 1000000;
	this->mRunTime = 0;

	rFrameStart = new FrameStartEvent();
	rFrameEnd = new FrameEndEvent();

	//
	// Set initial game state
	//

	this->State = new GameState();

	initGame();
	this->SystemStatus = this->SYSTEM_RUNNING;

	// Bind profiler data key
	/*BindEvent(new KeyDownEvent(InputKey('r', NULL)),
		Functor::tRef(new RawObjFunctor<Kernel>(this, &Kernel::DumpProfilerData)));*/

	while(this->SystemStatus == this->SYSTEM_RUNNING)
	{
		//
		// Update game state
		// XXX - maximum 64 FPS under windows due to poor timer resolution. Need fix!
		//	Note: HACKed in kernel.cpp
		//


		tGameTime newTime = (((tGameTime)microsec_clock::local_time().time_of_day().total_microseconds())
			/ 1000000) - this->mLocalStartTime;
	
#		if defined(_WIN32) && defined(TIME_HACK) // Ultra HAX! Devoted to Michael!
			static tGameTime skip = 0;
			if(newTime <= mLocalStartTime){
				skip += 0.0015625;
				newTime += skip;
			};
#		endif

		mTimeDelta = newTime - mRunTime;

		this->mRunTime = newTime;
		this->State->Update(this->mTimeDelta);
		this->ProcessEvent(rFrameStart);

		//
		// Exit if we have no tasks to run
		//

		if(this->RunQueue.GetSize() == 0)
		{
			this->SystemStatus = this->SYSTEM_EXITED;
			break;
		};


		//
		// Process the runqueue
		//

		while(this->RunQueue.GetSize())
		{
			pTask = this->RunQueue.PopTop();

			if(pTask->Status == pTask->RUNNING){
				// Execute
				pTask->SetProfiler(true);
				pTask->Run();
				pTask->SetProfiler(false);

				// Add to set of finished tasks
				pTask->Status = pTask->READY;
				tasks.insert(pTask);
			};
		};


		//
		// Requeue each task
		//

		for(task = tasks.begin(); task != tasks.end(); task++){
			if((*task)->Status == (*task)->READY)
				this->InsertTask(*task);
		};

		tasks.clear();

		this->ProcessEvent(rFrameEnd);
	};
};
